//FACEBOOK CLIENT CLASS IMPLEMENTATION
//BROWSER EMULATION ROUTINES FOR TRANSPARENT LOGIN AND OTHER TRICKS

#include "facebook_new.h"

#include "xmlParser.h"

#include "util/log.h"	


bool Facebook::CurUser_Login()
{
	m_ErrorDescription = "";
	m_InvalidLoginPassword = false;

	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "Facebook::CurUser_Login(). Current user was not set!"; return false;
	}

	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_Login(). Facebook::Init() wasn't called!"; return false;
	}

	if (m_pCurUser->m_LoggedIn == true)
	{
		return true;	
	}

	//STEP 1. Getting authentication token
	std::list<std::string> Params;
	std::string XmlResult="";

	if (HelperRequest_CallAPIMethod("facebook.auth.createToken", Params, XmlResult) == false)
	{
		m_ErrorDescription = "Facebook::CurUser_Login(). Step 1 error. Can't create auth token!"; return false;	
	}

	if (XmlResult.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_Login(). Step 1 error. Xml result is empty!"; return false;	
	}

	XMLNode Head = XMLNode::parseString(XmlResult.c_str(),NULL);
	XMLNode Node = Head.getChildNode(_T("auth_createToken_response"));

	if (Node.isEmpty())
	{
		m_ErrorDescription = "Facebook::CurUser_Login(). Step 1 error. Xml parsing error!"; return false;	
	}

	//remembering the token
	m_pCurUser->m_LoginToken = Node.getText();



	//STEP 2. Making log in. Black magic begins!

	//resetting the curl
	curl_easy_reset(m_pCurUser->m_pCurl);

	//creating url for get request
	std::string LoginGetRequestURL = m_LoginURL;

	//appending params
	LoginGetRequestURL.append("?api_key=");
	LoginGetRequestURL.append(m_AppKey);
	LoginGetRequestURL.append("&auth_token=");
	LoginGetRequestURL.append(m_pCurUser->m_LoginToken);
	LoginGetRequestURL.append("&v=");
	LoginGetRequestURL.append(FACEBOOK_APIVER);

	//providing url to curl
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_URL, LoginGetRequestURL.c_str());

	//telling curl it will be a GET request
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_HTTPGET, true);

	//enabling cookies
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_COOKIEFILE, "");

	//enabling redirections
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_FOLLOWLOCATION, 1);
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_MAXREDIRS, 100);

	//setting user agent - a subject to think about
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_USERAGENT, FACEBOOK_USERAGENT);

	//setting the request result writing
	std::string RequestResult = "";

	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_WRITEFUNCTION, Facebook::Write_Callback);
    curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_WRITEDATA, &RequestResult);	
	//setting callback func for this new user
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_NOPROGRESS, false);
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_PROGRESSFUNCTION, ProgressCallback); 

	//making the request
	CURLcode RequestResultCode;
	RequestResultCode = curl_easy_perform(m_pCurUser->m_pCurl);

	//printing request result in DEBUG
#ifdef _DEBUG
	Helper_SaveRequestResultToFile(RequestResult);
#endif

	//checking if request was ok
	if (RequestResultCode != 0)
	{
		m_ErrorDescription = "Facebook::CurUser_Login(). Step 2 error. Can't make HTTP request: initial GET request for login!"; return false;	
	}

	if (RequestResult.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_Login(). Step 2 error. Request result is empty(initial GET request for login)!"; return false;	
	}

	//making some checks to get a chance of finding out that something was changed on facebook site
	std::string::size_type FindLoc;

	//Check1.1. secure http login url
	FindLoc = RequestResult.find(FACEBOOK_SECURELOGINURL);
	if (FindLoc == std::string::npos)
	{
		m_FacebookSiteChanged = true;
		m_ErrorDescription = "Facebook::CurUser_Login(). STEP 2 ERROR. SECURE HTTP LOGIN URL WAS CHANGED!";
		return false;
	}

	//Check1.2. login info text
	FindLoc = RequestResult.find("Login to Facebook to enjoy the full functionality");
	if (FindLoc == std::string::npos)
	{
		m_FacebookSiteChanged = true;
		m_ErrorDescription = "Facebook::CurUser_Login(). STEP 2 ERROR. LOGIN PAGE WAS CHANGED!";
		return false;
	}
	
	//Check1.3. Main input fields
	//app token
	FindLoc = RequestResult.find("auth_token");
	if (FindLoc == std::string::npos)
	{
		m_FacebookSiteChanged = true;
		m_ErrorDescription = "Facebook::CurUser_Login(). STEP 2 ERROR. LOGIN PAGE WAS CHANGED: auth_token is missing!";
		return false;
	}
	//api key
	FindLoc = RequestResult.find("api_key");
	if (FindLoc == std::string::npos)
	{
		m_FacebookSiteChanged = true;
		m_ErrorDescription = "Facebook::CurUser_Login(). STEP 2 ERROR. LOGIN PAGE WAS CHANGED: api_key is missing!";
		return false;
	}
	//email
	FindLoc = RequestResult.find("email");
	if (FindLoc == std::string::npos)
	{
		m_FacebookSiteChanged = true;
		m_ErrorDescription = "Facebook::CurUser_Login(). STEP 2 ERROR. LOGIN PAGE WAS CHANGED: email is missing!";
		return false;
	}
	//pass
	FindLoc = RequestResult.find("pass");
	if (FindLoc == std::string::npos)
	{
		m_FacebookSiteChanged = true;
		m_ErrorDescription = "Facebook::CurUser_Login(). STEP 2 ERROR. LOGIN PAGE WAS CHANGED: pass is missing!";
		return false;
	}
	//charset_test
	FindLoc = RequestResult.find("charset_test");
	if (FindLoc == std::string::npos)
	{
		m_FacebookSiteChanged = true;
		m_ErrorDescription = "Facebook::CurUser_Login(). STEP 2 ERROR. LOGIN PAGE WAS CHANGED: charset_test is missing!";
		return false;
	}

	//after the checks we assume that it is safe to continue

	//now we prepare curl to make http POST request to secure login url
	
	//resetting the curl
	curl_easy_reset(m_pCurUser->m_pCurl);

	//setting request url
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_URL, FACEBOOK_SECURELOGINURL);

	//setting SSL params - the ones that work
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_SSL_VERIFYPEER, 0);
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_SSL_VERIFYHOST, 0);

	//aggregating POST params
	std::string POSTparams = "";

	//version
	POSTparams.append("version=");
	POSTparams.append(FACEBOOK_APIVER);
	//token
	POSTparams.append("&auth_token=");
	POSTparams.append(m_pCurUser->m_LoginToken);
	//api key
	POSTparams.append("&api_key=");
	POSTparams.append(m_AppKey);
	//email
	POSTparams.append("&email=");
	POSTparams.append(m_pCurUser->m_UserEmail);
	//password
	POSTparams.append("&pass=");
	POSTparams.append(m_pCurUser->m_UserPassword);
	//chareset test
	POSTparams.append("&charset_test=");
	POSTparams.append("%E2%82%AC%C2%B4%E2%82%AC%C2%B4%E6%B0%B4%D0%94%D0%84");

	//telling curl that it will be a POST request and setting POST fields
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_HTTPPOST, true );
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_POSTFIELDS, POSTparams.c_str());

	//enabling redirections
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_FOLLOWLOCATION, 1);
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_MAXREDIRS, 100);

	//setting user agent - a subject to think about
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_USERAGENT, FACEBOOK_USERAGENT);

	//setting the request result writing
	RequestResult = "";

	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_WRITEFUNCTION, Facebook::Write_Callback);
    curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_WRITEDATA, &RequestResult);	
	//setting callback func for this new user
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_NOPROGRESS, false);
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_PROGRESSFUNCTION, ProgressCallback); 

	//making the request
	RequestResultCode = curl_easy_perform(m_pCurUser->m_pCurl);

	//printing request result in DEBUG
#ifdef _DEBUG
	Helper_SaveRequestResultToFile(RequestResult);
#endif

	//checking if request was ok
	if (RequestResultCode != 0)
	{
		m_ErrorDescription = "Facebook::CurUser_Login(). Step 2 error. Can't make HTTP request: secure POST request for login!"; return false;	
	}

	if (RequestResult.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_Login(). Step 2 error. Request result is empty(secure POST request for login)!"; return false;	
	}

	//checking if login is complete
	FindLoc = RequestResult.find("You may now close this window and return to the application.");
	if (FindLoc != std::string::npos)
	{
		m_ErrorDescription = "";
		return Helper_CreateSession();
	}

	//checking if login/password was incorrect
	FindLoc = RequestResult.find("Incorrect email/password combination.");
	if (FindLoc != std::string::npos)
	{
		m_InvalidLoginPassword = true;
		m_ErrorDescription = "Facebook::CurUser_Login(). Step 2 error. User provided incorrect email/password.";
		return false;
	}

	//STEP 3. Approving the application.

	//checking if we've got a right page

	//Check 2.1
	FindLoc = RequestResult.find("confirmation_login_content");
	if (FindLoc == std::string::npos)
	{
		m_FacebookSiteChanged = true;
		m_ErrorDescription = "Facebook::CurUser_Login(). STEP 3 ERROR. Confirmation page was changed! no confirmation_login_content!";
		return false;
	}

	//Check 2.2
	FindLoc = RequestResult.find(FACEBOOK_DESKTOPAPP_APPROVEMENTURL);
	if (FindLoc == std::string::npos)
	{
		m_FacebookSiteChanged = true;
		m_ErrorDescription = "Facebook::CurUser_Login(). STEP 3 ERROR. Desktop app approvement URL was changed!";
		return false;
	}

	//now we assume that the page is right and we continue

	//extracting post form id value
	FindLoc = RequestResult.find("post_form_id");
	FindLoc = RequestResult.find("value", FindLoc);
	char TempChars[2] = {34, 0};
	FindLoc = RequestResult.find(TempChars, FindLoc);
	if (FindLoc == std::string::npos)
	{
		m_FacebookSiteChanged = true;
		m_ErrorDescription = "Facebook::CurUser_Login(). STEP 3 ERROR. Error in extracting post form id value!";
		return false;
	}

	std::string PostFormID = RequestResult.substr(FindLoc+1, 32);

	//now we prepare curl to make http POST request to desktop app confirmation url

	//resetting the curl
	curl_easy_reset(m_pCurUser->m_pCurl);

	//setting request url
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_URL, FACEBOOK_DESKTOPAPP_APPROVEMENTURL);

	//aggregating POST params
	POSTparams = "";

	//grant_perm
	POSTparams.append("grant_perm=1");
	//next
	POSTparams.append("&next=");
	//api key
	POSTparams.append("&api_key=" + m_AppKey);
	//auth_token
	POSTparams.append("&auth_token=" + m_pCurUser->m_LoginToken);
	//post form id
	POSTparams.append("&post_form_id=" + PostFormID);

	//telling curl that it will be a POST request and setting POST fields
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_HTTPPOST, true );
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_POSTFIELDS, POSTparams.c_str());

	//enabling redirections
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_FOLLOWLOCATION, 1);
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_MAXREDIRS, 100);

	//setting user agent - a subject to think about
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_USERAGENT, FACEBOOK_USERAGENT);

	//setting the request result writing
	RequestResult = "";

	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_WRITEFUNCTION, Facebook::Write_Callback);
    curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_WRITEDATA, &RequestResult);	
	//setting callback func for this new user
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_NOPROGRESS, false);
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_PROGRESSFUNCTION, ProgressCallback); 

	//making the request
	RequestResultCode = curl_easy_perform(m_pCurUser->m_pCurl);

	//checking if request was ok
	if (RequestResultCode != 0)
	{
		m_ErrorDescription = "Facebook::CurUser_Login(). Step 3 error. Can't make HTTP request: POST request for desktop app confirmation page!"; return false;	
	}

	if (RequestResult.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_Login(). Step 3 error. Request result is empty(POST request for desktop app confirmation page)!"; return false;	
	}

	//checking if login is complete
	FindLoc = RequestResult.find("You may now close this window and return to the application.");
	if (FindLoc != std::string::npos)
	{
		m_ErrorDescription = "";
		return Helper_CreateSession();
	}

	//got here because something was wrong with desktop app approvement
	m_ErrorDescription = "Facebook::CurUser_Login(). Step 3 error. Desktop app approvement failed.";
	return false;

}//bool Facebook::CurUser_Login()




bool Facebook::AddApplicationToProfile(const std::string& AppToAddKey, bool AllowProfile, bool AllowNavLink, bool AllowFeeds, bool AllowProfileActions)
{

	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "Facebook::AddApplicationToProfile(). Current user was not set!"; return false;
	}

	if (m_pCurUser->m_LoggedIn == false)
	{
		m_ErrorDescription = "Facebook::AddApplicationToProfile(). Current user was not logged in!"; return false;
	}

	if (m_pCurUser->m_UserProfileInfo.Name.size() < 1)
	{
		m_ErrorDescription = "Facebook::AddApplicationToProfile(). Current user profile info must be fetched before calling this method!"; return false;
	}

	//STEP 1. Making initial GET request. Black magic begins!

	//resetting the curl
	curl_easy_reset(m_pCurUser->m_pCurl);

	//creating url for get request
	std::string GetRequestURL = FACEBOOK_ADDAPPURL;

	//appending params
	GetRequestURL.append("?api_key=");
	GetRequestURL.append(AppToAddKey);
	GetRequestURL.append("&ref=pd");   //used as in browser


	//providing url to curl
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_URL, GetRequestURL.c_str());

	//telling curl it will be a GET request
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_HTTPGET, true);

	//enabling cookies
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_COOKIEFILE, "");

	//enabling redirections
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_FOLLOWLOCATION, 1);
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_MAXREDIRS, 100);

	//setting user agent - a subject to think about
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_USERAGENT, FACEBOOK_USERAGENT);

	//setting the request result writing
	std::string RequestResult = "";

	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_WRITEFUNCTION, Facebook::Write_Callback);
    curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_WRITEDATA, &RequestResult);	
	//setting callback func for this new user
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_NOPROGRESS, false);
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_PROGRESSFUNCTION, ProgressCallback); 

	//making the request
	CURLcode RequestResultCode;
	RequestResultCode = curl_easy_perform(m_pCurUser->m_pCurl);

	//checking if request was ok
	if (RequestResultCode != 0)
	{
		m_ErrorDescription = "Facebook::AddApplicationToProfile(). Step 1 error. Can't make HTTP request: initial GET request!"; return false;	
	}

	if (RequestResult.size() < 1)
	{
		m_ErrorDescription = "Facebook::AddApplicationToProfile(). Step 1 error. Request result is empty(initial GET request)!"; return false;	
	}

	//checking if app was already added
	std::string::size_type FindLoc;

	FindLoc = RequestResult.find("You've already added");

	if (FindLoc != std::string::npos)
	{
		//app was already added
		return true;
	}


	//making some tests to be sure that the facebook structure wasn't changed
	FindLoc = RequestResult.find("to your Facebook account?");

	if (FindLoc == std::string::npos)
	{
		m_ErrorDescription = "Facebook::AddApplicationToProfile(). Step 1 error. Request result is a wrong page! Something was changed on facebook!"; return false;	
		m_FacebookSiteChanged = true;
		return false;
	}

	FindLoc = RequestResult.find("Allow this application to");

	if (FindLoc == std::string::npos)
	{
		m_ErrorDescription = "Facebook::AddApplicationToProfile(). Step 1 error. Request result is a wrong page! Something was changed on facebook!"; return false;	
		m_FacebookSiteChanged = true;
		return false;
	}


	//STEP 2. Making final POST request.

	//extracting post form id value
	FindLoc = RequestResult.find("post_form_id");
	FindLoc = RequestResult.find("value", FindLoc);
	char TempChars[2] = {34, 0};
	FindLoc = RequestResult.find(TempChars, FindLoc);
	if (FindLoc == std::string::npos)
	{
		m_FacebookSiteChanged = true;
		m_ErrorDescription = "Facebook::AddApplicationToProfile(). STEP 1 ERROR. Error in extracting post form id value!";
		return false;
	}

	std::string PostFormID = RequestResult.substr(FindLoc+1, 32);

	//resetting the curl
	curl_easy_reset(m_pCurUser->m_pCurl);

	std::string POSTurl = FACEBOOK_ADDAPPURL;
	POSTurl.append("?next=");

	//setting request url
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_URL, POSTurl.c_str());

	//aggregating POST params
	std::string POSTparams = "";

	//allow_profile
	if (AllowProfile == true)
		POSTparams.append("allow_profile=on");
	//allow_nav_link
	if (AllowNavLink == true)
		POSTparams.append("&allow_nav_link=on");
	//allow_feeds
	if (AllowFeeds == true)
		POSTparams.append("&allow_feeds=on");
	//allow_profile_actions
	if (AllowProfileActions == true)
		POSTparams.append("&allow_profile_actions=on");
	
	//post form id
	POSTparams.append("&post_form_id=" + PostFormID);

	//api key
	POSTparams.append("&api_key=" + AppToAddKey);

	//install
	POSTparams.append("&install=1");

	//ref add
	POSTparams.append("&refadd=pd");

	//telling curl that it will be a POST request and setting POST fields
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_HTTPPOST, true );
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_POSTFIELDS, POSTparams.c_str());

	//enabling cookies
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_COOKIEFILE, "");

	//enabling redirections
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_FOLLOWLOCATION, 1);
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_MAXREDIRS, 100);

	//setting user agent - a subject to think about
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_USERAGENT, FACEBOOK_USERAGENT);

	//setting the request result writing
	RequestResult = "";

	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_WRITEFUNCTION, Facebook::Write_Callback);
    curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_WRITEDATA, &RequestResult);	
	//setting callback func for this new user
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_NOPROGRESS, false);
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_PROGRESSFUNCTION, ProgressCallback); 

	//making the request
	RequestResultCode = curl_easy_perform(m_pCurUser->m_pCurl);

	//checking if request was ok
	if (RequestResultCode != 0)
	{
		m_ErrorDescription = "Facebook::AddApplicationToProfile(). Step 2 error. Can't make HTTP request: POST request for desktop app confirmation page!"; return false;	
	}

	if (RequestResult.size() < 1)
	{
		m_ErrorDescription = "Facebook::AddApplicationToProfile(). Step 2 error. Request result is empty(POST request for desktop app confirmation page)!"; return false;	
	}


	//now we must get a profile page

	//need to be checked out

	FindLoc = RequestResult.find(m_pCurUser->m_UserProfileInfo.Name);

	if (FindLoc == std::string::npos)
	{
		m_FacebookSiteChanged = true;
		m_ErrorDescription = "Facebook::AddApplicationToProfile(). STEP 2 ERROR. Wrong!";
		return false;
	}
	else
	{
		m_ErrorDescription = "";
		return true;
	}


}//bool Facebook::AddApplicationToProfile(const std::string& AppToAddKey, bool AllowProfile, bool AllowNavLink, bool AllowFeeds, bool AllowProfileActions)









